package model;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.StringTokenizer;

import lucene.PaperSearcher;

import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.TermFreqVector;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.store.FSDirectory;

import data.TestUserSelection;

public class ContentbasedBayesianPredictor {
	private String user;
	private HashSet<String> holdoutPapers = new HashSet<String>();
	private HashSet<String> usedTags = new HashSet<String>();
	private HashMap<String, Double> tagTermMap = new HashMap<String, Double>();
	private HashMap<String, Double> tagProbMap = new HashMap<String, Double>();
	private IndexReader reader;
	private IndexReader generalReader;
	private PaperSearcher searcher;
	private PaperSearcher generalSearcher;

	public ContentbasedBayesianPredictor(String u, IndexReader gReader, PaperSearcher gSearcher) throws Exception {
		this.generalSearcher = gSearcher;
		this.generalReader = gReader;
		this.user = u;
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\testIndex\\";

		File indexDir = new File(path);
		IndexReader reader = IndexReader.open(FSDirectory.open(indexDir), true);
		this.reader = reader;
		this.searcher = new PaperSearcher(indexDir);

		initHoldoutPapers();
		loadTagTermMap();
		System.out.println("tagTermMap size\t" + tagTermMap.entrySet().size());
		loadTagProbMap();
	}
	
	public ContentbasedBayesianPredictor(String u) throws Exception{
		this.user = u;
		initHoldoutPapers();
		loadTagProbMap();
	}

	private void initHoldoutPapers() throws Exception {
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\test.txt";
		BufferedReader reader = new BufferedReader(new FileReader(path));
		String line = null;
		while ((line = reader.readLine()) != null) {
			line = line.trim();
			holdoutPapers.add(line);
		}
	}
	
	public void clear() throws Exception{
		reader.close();
	}

	private void loadTagProbMap() throws Exception {
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\tagProb.txt";
		BufferedReader reader = new BufferedReader(new FileReader(path));
		String line = null;
		while ((line = reader.readLine()) != null) {
			line = line.trim();
			String[] tokens = line.split("\t");
			String tag = tokens[0];
			usedTags.add(tag);
			double s = Double.valueOf(tokens[1]);
			tagProbMap.put(tag, s);
		}

	}

	private void loadTagTermMap() throws Exception {
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\tag_term_map.txt";
		BufferedReader reader = new BufferedReader(new FileReader(path));
		String line = null;
		while ((line = reader.readLine()) != null) {
			line = line.trim();
			String[] tokens = line.split("\t");
			String tag = tokens[0];
			String term = tokens[1];
			double s = Double.valueOf(tokens[2]);
			tagTermMap.put(tag + "\t" + term, s);
		}

	}

	public void predictTitle() throws Exception {
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\result\\title\\";
		File dir = new File(path);
		if(!dir.exists()) {
			dir.mkdirs();
		}
		for (String testCase : holdoutPapers) {
			String testCasePath = path + testCase + ".txt";
			int docId = searcher.retrieveDocByPid(testCase);
			String text = reader.document(docId).get("content");
			//TermFreqVector tfv = reader.getTermFreqVector(docId, "content");
			//String[] terms = tfv.getTerms();
			/*
			String title = "";
			for(String t : terms) {
				title = title + " " + t;
			}
			*/
			//System.out.println(testCase + "\ttitle\t" + title);
		    writeTextResult(text, testCasePath);
		}
	}
	
	public void predictAuthor() throws Exception{

		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\result\\author_bayesian\\";
		File dir = new File(path);
		if(!dir.exists()) {
			dir.mkdirs();
		}
		for (String testCase : holdoutPapers) {
			String testCasePath = path + testCase + ".txt";
			int docId = generalSearcher.retrieveDocByPid(testCase);
			String authors = generalReader.document(docId).get("author");
			
			StringTokenizer tokens = new StringTokenizer(authors);
			List<LinkedHashMap<String,Double>> list = new ArrayList<LinkedHashMap<String,Double>>();
			double authorNum = 0.0;
			while(tokens.hasMoreTokens()) {
				String au = tokens.nextToken();
				ScoreDoc[] docs = generalSearcher.retrieveResultsByAuthorId(au);
				if(docs.length == 0)
					continue;
				List<LinkedHashMap<String,Double>> mapList = new ArrayList<LinkedHashMap<String,Double>>();
				for(ScoreDoc doc : docs) {
					int did = doc.doc;
					String text = generalReader.document(did).get("content");
					LinkedHashMap<String,Double> map = compTagScoreMap(text); 
					mapList.add(map);
				}
				/*
				 * first normalized
				 */
				LinkedHashMap<String, Double> nMap = normalizedTagScoreMap(mapList,docs.length);
				list.add(nMap);
				authorNum ++;
			}
			
			/*
			 * second normalized
			 */
			if(authorNum == 0)
				continue;
			LinkedHashMap<String, Double> resultMap = normalizedTagScoreMap(list, authorNum);
			
			writeAuthorResult(resultMap,testCasePath);
		}
	
	}
	
	private LinkedHashMap<String, Double> normalizedTagScoreMap(
			List<LinkedHashMap<String, Double>> mapList, double normalizedSize)
			throws Exception {
		LinkedHashMap<String, Double> nMap = new LinkedHashMap<String, Double>();

		for (String usedTag : usedTags) {
			double score = 0.0;
			for (LinkedHashMap<String, Double> map : mapList) {
				score += map.get(usedTag);
			}
			score = score / normalizedSize;
			nMap.put(usedTag, score);
		}
		return nMap;
	}
	
	private LinkedHashMap<String, Double> compTagScoreMap(String text)
			throws Exception {
		LinkedHashMap<String, Double> map = new LinkedHashMap<String, Double>();
		for (String usedTag : usedTags) {
			double usedTagProb = tagProbMap.get(usedTag);
			double prob = 0;
			StringTokenizer tokens = new StringTokenizer(text);
			while (tokens.hasMoreTokens()) {
				String term = tokens.nextToken();
				Double termTagProb = tagTermMap.get(usedTag + "\t" + term);
				if (termTagProb == null) {
					termTagProb = 0.0;

					// termTagProb =
				}
				termTagProb = Math.log1p(termTagProb) / Math.log(2);
				prob = prob + termTagProb;
			}
			double score = (Math.log1p(usedTagProb) / Math.log(2)) + prob;

			map.put(usedTag, score);
			// writer.write(usedTag + "\t" + score);
		}
		return map;
	}
	
	private void writeAuthorResult(LinkedHashMap<String, Double> nMap, String testCasePath) throws Exception{

		BufferedWriter writer = new BufferedWriter(new FileWriter(testCasePath));
		List<RelevantNode> results = new ArrayList<RelevantNode>();
		try{
			for(String usedTag : usedTags) {				
				Double score = nMap.get(usedTag);
				if(score.isNaN()) {
					System.err.println("err\t" + testCasePath + "\tisNaN");
					return;
				}
				RelevantNode node = new RelevantNode(usedTag,score);
				results.add(node);
				//writer.write(usedTag + "\t" + score);
			}
			Collections.sort(results);
			for(RelevantNode rn : results) {
				String t = rn.getPid();
				double v = rn.getScore();
				writer.write(t + "\t" + v);
				writer.newLine();
			}			
		} finally{
			writer.flush();
			writer.close();
		}
		
		
	
	}
	
	private void writeTextResult(String text, String testCasePath) throws Exception{
		BufferedWriter writer = new BufferedWriter(new FileWriter(testCasePath));
		List<RelevantNode> results = new ArrayList<RelevantNode>();
		try{
			for(String usedTag : usedTags) {
				double usedTagProb = tagProbMap.get(usedTag);
				double prob = 0;
				StringTokenizer tokens = new StringTokenizer(text);
				while(tokens.hasMoreTokens()) {
					String term = tokens.nextToken();
					Double termTagProb = tagTermMap.get(usedTag + "\t" + term);
					if(termTagProb == null) {
						termTagProb = 0.0;
						
						//termTagProb = 
					} 
					termTagProb = Math.log1p(termTagProb)/Math.log(2);
					prob = prob + termTagProb;
				}
				Double score = (Math.log1p(usedTagProb)/Math.log(2)) + prob;
				if(score.isNaN()) {
					System.err.println("err\t" + testCasePath + "\tisNaN");
				}
				RelevantNode node = new RelevantNode(usedTag,score);
				results.add(node);
				//writer.write(usedTag + "\t" + score);
			}
			Collections.sort(results);
			for(RelevantNode rn : results) {
				String t = rn.getPid();
				double v = rn.getScore();
				writer.write(t + "\t" + v);
				writer.newLine();
			}			
		} finally{
			writer.flush();
			writer.close();
		}
		
		
	}
	
	private HashMap<String,Double> retrieveTitleTagRelevanceMap(String testCase) throws Exception{
		HashMap<String,Double> titleMap = new HashMap<String, Double>();
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\result\\title\\" + testCase + ".txt";
		BufferedReader reader = new BufferedReader(new FileReader(path));
		String line = null;
		while((line = reader.readLine()) != null) {
			line = line.trim();
			String[] tokens = line.split("\t");
			String tag = tokens[0];
			double rel = Double.valueOf(tokens[1]);
			titleMap.put(tag, rel);
		}
		return titleMap;
	}
	
	private HashMap<String,Double> retrieveAuthorTagRelevanceMap(String testCase) throws Exception{

		HashMap<String,Double> map = new HashMap<String, Double>();
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\result\\author_bayesian\\" + testCase + ".txt";
		BufferedReader reader = new BufferedReader(new FileReader(path));
		String line = null;
		while((line = reader.readLine()) != null) {
			line = line.trim();
			String[] tokens = line.split("\t");
			String tag = tokens[0];
			double rel = Double.valueOf(tokens[1]);
			map.put(tag, rel);
		}
		return map;			
	}
	
	public void hybrid(int step) throws Exception{
		double factor = 0.1 * step;
		String path = "D:\\CityU\\project\\bibsonomy\\experiment\\" + user
				+ "\\result\\hybrid_title_author_bayesian\\" + step + "\\";
		File dir = new File(path);
		if(!dir.exists()) {
			dir.mkdirs();
		}
						
		for (String testCase : holdoutPapers) {
			String testCasePath = path + testCase + ".txt";
			HashMap<String,Double> titleRelevanceMap =  retrieveTitleTagRelevanceMap(testCase);
			HashMap<String,Double> authorRelevanceMap = retrieveAuthorTagRelevanceMap(testCase);
			writeHybridResults(titleRelevanceMap, authorRelevanceMap, testCasePath, factor);
		}
		
	}
	
	private void writeHybridResults(HashMap<String, Double> titleRelevanceMap,
			HashMap<String, Double> authorRelevanceMap, String testCasePath,
			double factor) throws Exception {
		
		List<RelevantNode> results = new ArrayList<RelevantNode>();

		for (String usedTag : usedTags) {
			double tRel = titleRelevanceMap.get(usedTag);
			double aRel = authorRelevanceMap.get(usedTag);
			double score = (1 - factor) * tRel + factor * aRel;
			RelevantNode node = new RelevantNode(usedTag, score);
			results.add(node);
		}

		Collections.sort(results);
		BufferedWriter writer = new BufferedWriter(new FileWriter(testCasePath));
		try {
			for (RelevantNode rn : results) {
				String t = rn.getPid();
				double v = rn.getScore();
				writer.write(t + "\t" + v);
				writer.newLine();
			}
		} finally {
			writer.flush();
			writer.close();
		}
	}
	
	
	
	public HashSet<String> getUsedTags() {
		return usedTags;
	}

	public static void main(String[] args) throws Exception{
		//String user = "1";
		List<String> users = TestUserSelection.retrieveTestUsers();
		
		File indexDir = new File("D:\\CityU\\project\\bibsonomy\\"
				+ "\\BibIndex\\");
		PaperSearcher gSearcher = new PaperSearcher(indexDir);
		IndexReader gReader = gSearcher.getReader();
		
/*		
		for (String user : users) {
			
			System.out.println("run test user\t" + user);
			ContentbasedBayesianPredictor app = new ContentbasedBayesianPredictor(user, gReader, gSearcher);
			//app.predictTitle();
			app.predictAuthor();
			app.clear();
		}
*/
		
		for(String user : users) {
			ContentbasedBayesianPredictor app = new ContentbasedBayesianPredictor(user);
			System.out.println("run test user\t" + user + "\tusedTag num\t" + app.getUsedTags().size());
			for(int i = 1; i <= 9 ; i++) {
				//double factor = 0.1*i;
				app.hybrid(i);
			}			
		}
		
	}

}
